/**
 * 装饰模式：动态地给一个对象添加一些额外的功能，它是通过创建一个包装对象，也就是装饰来包裹真实的对象。新增加功能来说，装饰器模式比生产子类更加灵活。
 * 以下情形考虑使用装饰模式：
 * 1. 需要扩展一个类的功能，或给一个类添加附加职责。
 * 2. 需要动态的给一个对象添加功能，这些功能可以再动态的撤销。
 * 3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能，从而使继承关系变的不现实。
 * 4. 当不能采用生成子类的方法进行扩充时。一种情况是，可能有大量独立的扩展，为支持每一种组合将产生大量的子类，使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏，或类定义不能用于生成子类。
 * 缺点：
 * 1. 各层的初始化配置代码看上去可能会很糟糕。
 */
#pragma once

#include <iostream>
#include <memory>

using namespace std;

namespace decorator
{
/*
* 关键代码：1、Component 类充当抽象角色，不应该具体实现。2、修饰类引用和继承 Component 类，具体扩展类重写父类方法。
*/
class Component
{
public:
    virtual ~Component() {}
    virtual string operation() const = 0;
};
// 具体构件角色：定义一个将要接收附加责任的类。
class ConcreteComponent : public Component
{
public:
    string operation() const override {
        return "ConcreteComponent";
    }
};
// 装饰角色：持有一个构件（Component）对象的实例，并实现一个与抽象构件接口一致的接口。
class Decorator : public Component {
public:
    Decorator(shared_ptr<Component> component) : m_component(component) {}
    string operation() const override {
        return m_component->operation();
    }
protected:
    shared_ptr<Component> m_component;
};

class ConcreteDecoratorA : public Decorator
{
public:
    ConcreteDecoratorA(shared_ptr<Component> component) : Decorator(component) {}
    string operation() const override {
        return "ConcreteDecoratorA(" + Decorator::operation() + ")";
    }
};

class ConcreteDecoratorB : public Decorator
{
public:
    ConcreteDecoratorB(shared_ptr<Component> component) : Decorator(component) {}
    string operation() const override {
        return "ConcreteDecoratorB(" + Decorator::operation() + ")";
    }
};

void ClientCode()
{
    shared_ptr<Component> simple(new ConcreteComponent);
    cout << "Client: I've got a simple component:" << endl;
    cout << "RESULT: " << simple->operation() << endl;
    shared_ptr<Component> decorator1(new ConcreteDecoratorA(simple));
    shared_ptr<Component> decorator2(new ConcreteDecoratorB(decorator1));
    cout << "Client: Now I've got a decorated component:" << endl;
    cout << "RESULT: " << decorator2->operation() << endl;
}
}   // namespace decorator